package com.behome.netty.netty_D.bean;


import com.behome.netty.common.ByteConventerKit;
import com.behome.netty.common.util.CRCKit;

import java.nio.ByteBuffer;

public class Packet {

    public static final int MIN_LENGTH = 45;
    public static final int TAIL_LENGTH = 4;

    protected PacketHead head;

    protected PacketBody body;
    protected PacketTail tail;
    private byte[] data;
    protected byte[] signHead;
    protected byte[] sign;

    public Packet() {
    }

    public byte[] getData() {
        return this.data;
    }

    public void setData(byte[] data) {
        this.data = data;
    }

    public void setHead(PacketHead head) {
        this.head = head;
    }

    public void setBody(PacketBody body) {
        this.body = body;
    }

    public void setTail(PacketTail tail) {
        this.tail = tail;
    }

    public PacketHead getHead() {
        return this.head;
    }

    public PacketBody getBody() {
        return this.body;
    }

    public PacketTail getTail() {
        return this.tail;
    }

    public byte[] getSignHead() {
        return this.signHead;
    }

    public byte[] getSign() {
        return this.sign;
    }

    public byte[] serialize() {
        if (this.data != null && this.data.length > 0) {
            return this.data;
        } else {
            byte[] bodyData = this.body.serialize();
            byte[] headData = this.head.serialize();

            byte[] tmpData = new byte[headData.length + bodyData.length];
            System.arraycopy(headData, 0, tmpData, 0, headData.length);
            System.arraycopy(bodyData, 0, tmpData, headData.length, bodyData.length);
            byte[] signData = this.serializeSign(tmpData);
            ByteBuffer byteBuffer3 = ByteBuffer.allocate(headData.length + bodyData.length + signData.length);
            byteBuffer3.put(headData).put(bodyData).put(signData);
            byte[] crcData = this.getCrc32Value(byteBuffer3.array());
            byteBuffer3 = ByteBuffer.allocate(signData.length + crcData.length);
            byteBuffer3.put(signData).put(crcData);
            byte[] tailData = byteBuffer3.array();
            ByteBuffer byteBuffer = ByteBuffer.allocate(bodyData.length + headData.length + tailData.length);
            byteBuffer.put(headData).put(bodyData).put(tailData);
            return byteBuffer.array();
        }
    }

    private byte[] serializeSign(byte[] data) {
        return ByteConventerKit.shortToBytes((byte) 0);
//        //@TODO 是否签名
//        if (tail == null || !tail.isSign) {
//            return ByteConventerKit.shortToBytes((byte) 0);
//            //TODO 向平台发送数据 处理无签名签名数据都为0
////            byte[] ret = new byte[2 + 74];
////            byte[] signData = new byte[]{0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0};
////            System.arraycopy(ByteConventerKit.shortToBytes((byte)74),0, ret, 0, 2);
////            System.arraycopy(signData, 0, ret, 2, signData.length);
////            return  ret;
//        } else {
//            byte[] signData = CryptoFactory.getCrypto().signature(data);
//            byte[] ret = new byte[2 + signData.length];
//            System.arraycopy(ByteConventerKit.shortToBytes((new Integer(signData.length)).shortValue()), 0, ret, 0, 2);
//            System.arraycopy(signData, 0, ret, 2, signData.length);
//            return ret;
//        }
    }

    private byte[] getCrc32Value(byte[] data) {
        return ByteConventerKit.intToBytes2(CRCKit.crc32Calc(data));
    }


    public static Packet deserialize(byte[] data) {
        Packet ret = new Packet();
        if (data != null && data.length > 0) {
            if (crcValidate(data)) {
                ByteBuffer byteBuffer = ByteBuffer.wrap(data);
                byte[] headData = new byte[12];
                byteBuffer.get(headData);
                PacketHead head = PacketHead.deserialize(headData);
                if (head.getType() == PacketHead.PacketType.RESPONSE) {
                    ret.setHead(head);
                    ret.setBody(null);
                    ret.setTail(null);
                } else {
                    short packetLength = byteBuffer.getShort(10);
                    byte[] bodyData = new byte[packetLength - 12 - PacketTail.getLength(head.getSignFlag() == 1)];
                    byteBuffer.position(12);
                    byteBuffer.get(bodyData);
                    PacketBody body = PacketBody.deserialize(bodyData);
                    ret.setHead(head);
                    ret.setBody(body);
                    //去除CRC的
                    short signLength = byteBuffer.getShort();
                    byte[] tailByte = new byte[signLength];
                    byteBuffer.get(tailByte);
                    ByteBuffer headAndBodyBuffer = ByteBuffer.wrap(data);
                    byte[] headAndBodyData = new byte[packetLength - PacketTail.getLength(head.getSignFlag() == 1)];
                    headAndBodyBuffer.get(headAndBodyData);
                    PacketTail tail = PacketTail.deserialize(headAndBodyData, tailByte, head.getSignFlag() == 1);
                    ret.setTail(tail);
                }
            }
        }

        return ret;
    }

    private static boolean crcValidate(byte[] data) {
        byte[] pkg = new byte[data.length - 4];
        System.arraycopy(data, 0, pkg, 0, pkg.length);
        byte[] crc32ValData = new byte[4];
        System.arraycopy(data, data.length - 4, crc32ValData, 0, crc32ValData.length);
        int crcCalcVal = CRCKit.crc32Calc(pkg);
        int crcVal = ByteConventerKit.bytesToInt2(crc32ValData, 0);
        boolean ret = crcCalcVal == crcVal;
        if (!ret) {
            System.out.println("CRC32校验错误(计算值为" + crcCalcVal + ",待校验值为" + crcVal + ").");
        }

        return ret;
    }

    public String toString() {
        if (this.head != null) {
            StringBuilder appder = new StringBuilder();
            if (this.head.getType() == PacketHead.PacketType.RESPONSE) {
                appder.append("(packetType:应答指令").append(",sessionId:").append(this.head.getSessionId()).append(")");
                return appder.toString();
            }

            if (this.body != null && this.body.getBusinessCmd() != null) {
                appder.append("(sourceCode:").append(this.body.getSourceCode()).append(",sessionId:").append(this.head.getSessionId()).append(",cmdInfo:").append(this.body.getBusinessCmd().toString());
                return appder.toString();
            }
        }

        return null;
    }
}
